﻿using AutoMapper;
using JuCheap.Core.Data.Entity;
using JuCheap.Core.Infrastructure;
using JuCheap.Core.Infrastructure.Exceptions;
using JuCheap.Core.Infrastructure.Extentions;
using JuCheap.Core.Infrastructure.Utilities;
using JuCheap.Core.IRepository;
using JuCheap.Core.IService.AppService;
using JuCheap.Core.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace JuCheap.Core.Service.AppService
{
    /// <summary>
    /// 用户扩展服务
    /// </summary>
    public class UserService : IUserService
    {
        private readonly ISysCompanyRepository _companyRepository;
        private readonly ISysUserRepository _sysUserRepository;
        private readonly ISysUserRoleRepository _sysUserRoleRepository;
        private readonly ISysRoleMenuRepository _sysRoleMenuRepository;
        private readonly ISysMenuRepository _sysMenuRepository;
        private readonly ISysLoginLogRepository _loginLogRepository;
        private readonly IMapper _mapper;
        private readonly ISysDepartmentRepository _sysDepartmentRepository;
        private readonly ICurrentUserService _currentUserService;

        /// <summary>
        /// ctor
        /// </summary>
        public UserService(ISysLoginLogRepository loginLogRepository, ISysCompanyRepository companyRepository, ISysUserRepository sysUserRepository, IMapper mapper,
            ISysUserRoleRepository sysUserRoleRepository,
            ISysRoleMenuRepository sysRoleMenuRepository,
            ISysMenuRepository sysMenuRepository,
            ISysDepartmentRepository sysDepartmentRepository,
            ICurrentUserService currentUserService)
        {
            _loginLogRepository = loginLogRepository;
            _companyRepository = companyRepository;
            _sysUserRepository = sysUserRepository;
            _mapper = mapper;
            _sysUserRoleRepository = sysUserRoleRepository;
            _sysRoleMenuRepository = sysRoleMenuRepository;
            _sysMenuRepository = sysMenuRepository;
            _sysDepartmentRepository = sysDepartmentRepository;
            _currentUserService = currentUserService;
        }

        /// <summary>
        /// 登陆
        /// </summary>
        /// <param name="loginAccount">账号</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public async Task<CurrentUserDto> LoginAsync(string loginAccount, string password)
        {
            var currentUser = new CurrentUserDto() { LoginName = loginAccount };
            var user = await _sysUserRepository.FindAsync(x => x.LoginName == loginAccount);
            var loginResult = string.Empty;
            if (user == null)
            {
                loginResult = "登陆账号错误";
            }
            else
            {
                currentUser.UserId = user.Id;
                if (user.IsDeleted)
                {
                    loginResult = "账号已被删除";
                }
                var passwordMd5 = password.ToMd5(user.SecurityKey);
                if (!user.Password.IsEqual(passwordMd5, StringComparison.CurrentCulture))
                {
                    loginResult = "登陆密码错误";
                }

                var company = await _companyRepository.FindAsync(x => x.Id == user.CompanyId);
                _mapper.Map(user, currentUser);
                currentUser.CompanyName = company.CompanyName;
                currentUser.IsSystemCompany = company.IsSystem;
                currentUser.UserId = user.Id;
            }

            await WriteLoginLogAsync(currentUser, loginResult);

            if (loginResult.IsNotBlank())
            {
                throw new BusinessException(loginResult);
            }

            return currentUser;
        }

        /// <summary>
        /// 写登陆日志
        /// </summary>
        /// <returns></returns>
        private async Task WriteLoginLogAsync(CurrentUserDto user, string loginResult)
        {
            try
            {
                if (loginResult.IsBlank())
                {
                    loginResult = $"登陆成功";
                }
                var log = new SysLoginLog
                {
                    Result = $"{user.LoginName},{loginResult}.",
                    CompanyId = user.CompanyId,
                    CreateUserId = user.UserId
                };
                await _loginLogRepository.InsertAsync(log);
            }
            catch { }
        }

        /// <summary>
        /// 创建用户
        /// </summary>
        /// <param name="userReq">用户请求信息</param>
        /// <returns>用户Id</returns>
        public async Task<string> CreateAsync(UserCreateRequestDto userReq)
        {
            if (await _sysUserRepository.ExistsAsync(x => x.LoginName == userReq.LoginName))
            {
                throw new BusinessException("登陆账号重复");
            }
            var user = _mapper.Map<SysUser>(userReq);
            user.SecurityKey = Guid.NewGuid().ToString("N").ToLower();
            user.Password = user.Password.ToMd5(user.SecurityKey);
            if (user.DepartmentId.IsNotBlank())
            {
                var department = await _sysDepartmentRepository.FindAsync(user.DepartmentId);
                user.DepartmentName = department.Name;
            }
            var userId = await _sysUserRepository.InsertAsync(user);
            return userId;
        }

        /// <summary>
        /// 修改用户
        /// </summary>
        /// <param name="userReq">用户请求信息</param>
        /// <returns>用户Id</returns>
        public async Task<string> UpdateAsync(UserEditRequestDto userReq)
        {
            if (await _sysUserRepository.ExistsAsync(x => x.Id != userReq.Id && x.LoginName == userReq.LoginName))
            {
                throw new BusinessException("登陆账号重复");
            }

            var user = await _sysUserRepository.FindAsync(userReq.Id);
            _mapper.Map(userReq, user);
            if (userReq.Password.IsNotBlank())
            {
                user.Password = userReq.Password.ToMd5(user.SecurityKey);
            }
            if (user.DepartmentId.IsNotBlank())
            {
                var department = await _sysDepartmentRepository.FindAsync(user.DepartmentId);
                user.DepartmentName = department.Name;
            }
            await _sysUserRepository.UpdateAsync(user);
            return user.Id;
        }

        /// <summary>
        /// 用户角色设置
        /// </summary>
        /// <param name="userRoleDto"></param>
        /// <returns></returns>
        public async Task SetRolesAsync(UserRoleSetRequestDto userRoleDto)
        {
            //删除以前的数据
            await _sysUserRoleRepository.DeleteAsync(x => x.UserId == userRoleDto.UserId);
            //重新添加数据
            var userRoleList = userRoleDto.RoleIds.Select(x => new SysUserRole
            {
                UserId = userRoleDto.UserId,
                RoleId = x
            }).ToList();
            await _sysUserRoleRepository.BatchInsertAsync(userRoleList);
        }

        /// <summary>
        /// 获取用户拥有的角色Id集合
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        public async Task<IEnumerable<string>> GetUserRoleIdsAysnc(string userId)
        {
            return await _sysUserRoleRepository.Queryable()
                .Where(x => x.UserId == userId).Select(x => x.RoleId).ToListAsync();
        }

        /// <summary>
        /// 获取用户拥有的菜单权限信息
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        public async Task<IEnumerable<SysMenuDto>> GetUserMenusAsync(string userId)
        {
            var user = await _sysUserRepository.FindAsync(userId);
            var menuQuery = _sysMenuRepository.Queryable(false);
            //管理员有所有的权限
            if (user.IsAdmin)
            {
                return await menuQuery.Select(x => new SysMenuDto
                {
                    Id = x.Id,
                    Name = x.Name,
                    IsMenu = x.IsMenu,
                    Code = x.Code,
                    Url = x.Url,
                    ParentId = x.ParentId,
                    Icon = x.Icon
                }).ToListAsync();
            }
            var userRoleIds = await GetUserRoleIdsAysnc(userId);
            var menuIds = await _sysRoleMenuRepository.Queryable()
                .Where(x => userRoleIds.Contains(x.RoleId)).Select(x => x.MenuId).ToListAsync();
            return await menuQuery
                .Where(x => menuIds.Contains(x.Id)).Select(x => new SysMenuDto
                {
                    Id = x.Id,
                    Name = x.Name,
                    IsMenu = x.IsMenu,
                    Code = x.Code,
                    Url = x.Url,
                    ParentId = x.ParentId,
                    Icon = x.Icon
                }).ToListAsync();
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="changePasswordDto"></param>
        /// <returns></returns>
        public async Task<bool> ChangePasswordAsync(UserChangePasswordDto changePasswordDto)
        {
            var user = await _sysUserRepository.FindAsync(changePasswordDto.Id);
            var oldPasswordKey = changePasswordDto.OldPassword.ToMd5(user.SecurityKey);
            if (!oldPasswordKey.IsEqual(user.Password))
            {
                throw new BusinessException("原始密码错误");
            }
            user.Password = changePasswordDto.NewPassword.ToMd5(user.SecurityKey);
            await _sysUserRepository.UpdateAsync(user, x => x.Password);
            return true;
        }

        /// <summary>
        /// 更新个人信息
        /// </summary>
        /// <param name="profile">基本资料</param>
        /// <returns></returns>
        public async Task<bool> UpdateProfileAsync(UserProfileUpdateDto profile)
        {
            var user = await _sysUserRepository.FindAsync(profile.Id);
            _mapper.Map(profile, user);
            await _sysUserRepository.UpdateAsync(user, x => x.Name, x => x.Email, x => x.Phone, x => x.Motto);
            return true;
        }

        /// <summary>
        /// 加载菜单树状结构
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <param name="loadAction">是否加载非菜单的数据</param>
        /// <param name="loadSystem">是否加载系统菜单的数据</param>
        /// <returns></returns>
        public async Task<List<MenuResponseDto>> GetMenuTreeAsync(string userId, bool loadAction, bool loadSystem)
        {
            Expression<Func<SysMenu, bool>> exp = x => true;
            exp = exp.AndIf(!loadAction, x => x.IsMenu)
                .AndIf(!loadSystem, x => !x.IsSystem);
            List<SysMenuDto> menuList = null;
            var menuQuery = _sysMenuRepository.Queryable(false).Where(exp);

            var user = await _sysUserRepository.FindAsync(userId);

            //管理员直接查看所有菜单
            if (user.IsAdmin)
            {
                menuList = await menuQuery.Select(x => new SysMenuDto
                {
                    Id = x.Id,
                    ParentId = x.ParentId,
                    Name = x.Name,
                    IsMenu = x.IsMenu,
                    Url = x.Url,
                    Icon = x.Icon,
                    Order = x.Order
                }).ToListAsync();
            }
            else
            {
                var query = from menu in menuQuery
                            join roleMenu in _sysRoleMenuRepository.Queryable() on menu.Id equals roleMenu.MenuId
                            join userRole in _sysUserRoleRepository.Queryable() on roleMenu.RoleId equals userRole.RoleId
                            where userRole.UserId == userId
                            select new SysMenuDto
                            {
                                Id = menu.Id,
                                ParentId = menu.ParentId,
                                Name = menu.Name,
                                IsMenu = menu.IsMenu,
                                Url = menu.Url,
                                Icon = menu.Icon,
                                Order = menu.Order
                            };

                menuList = await query.ToListAsync();
            }
            var treeList = DoGetChildren(string.Empty, menuList);
            return treeList;
        }

        /// <summary>
        /// 递归获取菜单树
        /// </summary>
        /// <param name="parentId">父级id</param>
        /// <param name="menuList">菜单原始列表</param>
        /// <returns></returns>
        private List<MenuResponseDto> DoGetChildren(string parentId, IEnumerable<SysMenuDto> menuList)
        {
            var children = menuList
                .WhereIf(parentId.IsBlank(), x => x.ParentId.IsBlank())
                .WhereIf(parentId.IsNotBlank(), x => x.ParentId == parentId)
                .OrderBy(x => x.Order)
                .Select(x => new MenuResponseDto
                {
                    Icon = x.Icon,
                    Key = x.Id,
                    Path = x.Url,
                    Selectable = false,
                    Title = x.Name,
                    Value = x.Id,
                }).ToList();
            foreach (var tree in children)
            {
                tree.Children = DoGetChildren(tree.Key, menuList);
            }
            return children;
        }

        /// <summary>
        /// 验证是否有权限
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <param name="permissionCode">权限唯一编码</param>
        /// <returns></returns>
        public async Task<bool> HasPermissionAsync(string userId, string permissionCode)
        {
            if (userId.IsBlank())
            {
                return false;
            }
            var user = await _sysUserRepository.FindAsync(userId);
            if (user.IsAdmin)
            {
                return true;
            }
            //级联查询
            var query = from roleMenu in _sysRoleMenuRepository.Queryable()
                        join userRole in _sysUserRoleRepository.Queryable() on roleMenu.RoleId equals userRole.RoleId
                        join menu in _sysMenuRepository.Queryable(false) on roleMenu.MenuId equals menu.Id
                        where userRole.UserId == userId && menu.Code == permissionCode
                        select roleMenu.MenuId;
            return await query.AnyAsync();
        }

        /// <summary>
        /// 用户分页扩展方法
        /// </summary>
        /// <param name="filter">基础分页参数</param>
        /// <returns></returns>
        public async Task<PagedResult<SysUserDto>> QueryPagesAsync(SysUserFilter filter)
        {
            var isSuperCompany = _currentUserService.IsSystemCompany();
            var query = from user in _sysUserRepository.Queryable(!isSuperCompany)
                        join company in _companyRepository.Queryable(!isSuperCompany) on user.CompanyId equals company.Id
                        select new SysUserDto
                        {
                            Id = user.Id,
                            Name = user.Name,
                            LoginName = user.LoginName,
                            IsAdmin = user.IsAdmin,
                            DepartmentId = user.DepartmentId,
                            DepartmentName = user.DepartmentName,
                            CompanyName = company.CompanyName,
                            CreateTime = user.CreateTime
                        };
            query = query.WhereIf(filter.Name.IsNotBlank(), x => x.Name.Contains(filter.Name))
                .WhereIf(filter.LoginName.IsNotBlank(), x => x.LoginName.Contains(filter.LoginName))
                .WhereIf(filter.IsAdmin.HasValue, x => x.IsAdmin == filter.IsAdmin.Value);

            return await query.QueryPagesAsync(filter);
        }
    }
}
